package com.liu.hooks;

import java.security.SecureRandom;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;

import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.conn.ssl.X509HostnameVerifier;

import com.liu.fake.TrustAllApacheSSLSocketFactory;
import com.liu.fake.TrustAllSSLSocketFactory;
import com.liu.fake.TrustAllX509TrustManager;
import com.liu.util.TrackerLoger;

import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.XposedHelpers;

public class SSLHook extends AbstractHook {

	public static final String TAG = "#SSLHook#";
	
	public static final String TYPE = "com.liu.hooks.SSLHook";
	
	public TrustManager[] trustManagers = new TrustManager[] {new TrustAllX509TrustManager()};
	
	public XC_MethodHook getSocketFactoryHook = new XC_MethodHook() {
		@Override
		protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
			param.setResult(TrustAllApacheSSLSocketFactory.getSocketFactory());
			TrackerLoger.log(TAG, "Hoooooo! Hook in getSocketFactoryHook!", TYPE, true);
			return;
		}
	};
	
	public XC_MethodHook sslContextInitHook = new XC_MethodHook() {
		@Override
		protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
			param.args[1] = trustManagers;
			TrackerLoger.log(TAG, "Hoooooo! Hook in sslContextInitHook!", TYPE, true);
		}
	};
	
	public XC_MethodHook hostNameVerifierHook = new XC_MethodHook() {
		@Override
		protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
			param.args[0] = SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER;
			TrackerLoger.log(TAG, "Hoooooo! Hook in hostNameVerifierHook!", TYPE, true);
		}
	};
	
	public XC_MethodHook setSSLSocketFactoryHook = new XC_MethodHook() {
		@Override
		protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
			param.args[0] = new TrustAllSSLSocketFactory();
			TrackerLoger.log(TAG, "Hoooooo! Hook in setSSLSocketFactoryHook!", TYPE, true);
		}
	};
	
	public XC_MethodHook schemeHook = new XC_MethodHook() {
		@Override
		protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
			if(((String)param.args[0]).equals("https")) {
				param.args[1] = new TrustAllApacheSSLSocketFactory(null);
				TrackerLoger.log(TAG, "Hoooooo! Hook in schemeHook!", TYPE, true);
			}
		}
	};
	
	public XC_MethodHook urlConnectionHook = new XC_MethodHook() {

		@Override
		protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
			param.setResult(new TrustAllSSLSocketFactory());
		}
		
	};
	
	@Override
	public void doHook() {
		XposedHelpers.findAndHookMethod(SSLSocketFactory.class, "getSocketFactory", getSocketFactoryHook);
		
		XposedHelpers.findAndHookMethod(SSLContext.class, "init", "javax.net.ssl.KeyManager[]", "javax.net.ssl.TrustManager[]", SecureRandom.class, sslContextInitHook);
		
		XposedHelpers.findAndHookMethod(SSLSocketFactory.class, "setHostnameVerifier", X509HostnameVerifier.class, hostNameVerifierHook);
		
		XposedHelpers.findAndHookMethod(HttpsURLConnection.class, "setHostnameVerifier", HostnameVerifier.class, hostNameVerifierHook);
		XposedHelpers.findAndHookMethod(HttpsURLConnection.class, "setDefaultHostnameVerifier", HostnameVerifier.class, hostNameVerifierHook);
		
		XposedHelpers.findAndHookMethod(HttpsURLConnection.class, "setSSLSocketFactory", javax.net.ssl.SSLSocketFactory.class, setSSLSocketFactoryHook);
		XposedHelpers.findAndHookMethod(HttpsURLConnection.class, "setDefaultSSLSocketFactory", javax.net.ssl.SSLSocketFactory.class, setSSLSocketFactoryHook);
		
//		XposedHelpers.findAndHookConstructor(Scheme.class, String.class, "org.apache.http.conn.scheme.SocketFactory", int.class, schemeHook);
		XposedBridge.hookAllConstructors(Scheme.class, schemeHook);
		
		XposedHelpers.findAndHookMethod(HttpsURLConnection.class, "getDefaultSSLSocketFactory", urlConnectionHook);
	}

	@Override
	public void setClassLoader(ClassLoader classloader) {
		this.classloader = classloader;
	}

}
